#pragma once
#include <pthread.h>
#include <functional>
#include <string>
#include <iostream>
#include <cassert>

namespace ThreadNs
{
    class Thread;

    struct Context
    {
        Thread *_this;
        void *_args;
    };

    class Thread
    {

        Thread(const Thread &) = delete;
        void operator = (const Thread &) = delete;

        const int NUM = 512;
        using func_t = std::function<void *(void *)>;

    public:
        Thread(func_t func, void *args = nullptr)
            : _func(func), _args(args)
        {
            char buffer[NUM];
            std::snprintf(buffer, sizeof(buffer), "thread-%d", threadnum++);
            _thread_name = buffer;

            start(func, args);
        }

        Thread()
        {
            char buffer[NUM];
            std::snprintf(buffer, sizeof(buffer), "thread-%d", threadnum++);
            _thread_name = buffer;
        }

        void start(func_t func, void *args = nullptr)
        {
            _func = func;
            _args = args;

            Context *con = new Context;
            con->_args = args;
            con->_this = this;

            int n = pthread_create(&_tid, nullptr, start_routine, con);
            assert(n == 0);
        }

        void join()
        {
            int n = pthread_join(_tid, nullptr);
            assert(n == 0);
        }

        std::string name()
        {
            return _thread_name;
        }

    private:
        static void *start_routine(void *args)
        {
            Context *con = static_cast<Context *>(args);
            void *ret = con->_this->run_routine(con->_args);
            delete con;
            return ret;
        }

        void *run_routine(void *args)
        {
            return _func(args);
        }

    private:
        std::string _thread_name;
        func_t _func;
        pthread_t _tid;
        void *_args;

        static int threadnum;
    };

    int Thread::threadnum = 1;
}